/*
一个简单的调试器示例，支持断点、单步执行、查看寄存器、查看内存等功能。

# 调试器命令示例：
# break 0x401123      # 在0x401123设置断点
# step                # 单步执行
# regs                # 查看寄存器
# mem 0x7ffd1234      # 查看内存
# run                 # 继续执行
# maps                # 显示内存映射
# help                # 显示帮助
# quit                # 退出调试器
*/


#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>    // 用于打开/proc/pid/maps

#define BREAKPOINT 0xCC  // int3 指令的机器码

typedef struct {
    long address;    // 断点地址
    long original;   // 原始指令
} Breakpoint;

Breakpoint *breakpoints[100];
int bp_count = 0;

void show_registers(pid_t pid);

// 附加到正在运行的进程
void attach(pid_t pid) {
    if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
        perror("ptrace attach");
        exit(1);
    }
    waitpid(pid, NULL, 0);  // 等待进程暂停
}

// 启动新进程并进入调试模式
void launch(const char *program) {
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程：允许被跟踪
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl(program, program, NULL);
    } else {
        // 父进程：等待子进程暂停
        waitpid(pid, NULL, 0);
    }
}

// 检查地址是否有效
int is_address_valid(pid_t pid, long addr) {
    char path[64];
    sprintf(path, "/proc/%d/maps", pid);
    
    FILE *fp = fopen(path, "r");
    if (!fp) {
        perror("打开内存映射文件失败");
        return 0;
    }
    
    int valid = 0;
    char line[256];
    while (fgets(line, sizeof(line), fp)) {
        unsigned long start, end;
        char perms[5];
        
        if (sscanf(line, "%lx-%lx %4s", &start, &end, perms) == 3) {
            // 检查地址是否在这个区域内
            if (addr >= start && addr < end) {
                // 检查是否有读权限 (至少需要r权限)
                if (strchr(perms, 'r')) {
                    valid = 1;
                    // 如果我们试图在这里设置断点，还需要检查是否可执行
                    if (strchr(perms, 'x')) {
                        valid = 2; // 2表示可读且可执行
                    }
                }
                break;
            }
        }
    }
    
    fclose(fp);
    return valid;
}

// 显示进程内存映射
void show_memory_maps(pid_t pid) {
    char path[64];
    sprintf(path, "/proc/%d/maps", pid);
    
    FILE *fp = fopen(path, "r");
    if (!fp) {
        perror("打开内存映射文件失败");
        return;
    }
    
    printf("内存区域:\n");
    printf("%-16s %-9s %-8s %s\n", "地址范围", "权限", "偏移量", "路径");
    
    char line[256];
    while (fgets(line, sizeof(line), fp)) {
        // 删除换行符
        line[strcspn(line, "\n")] = 0;
        printf("%s\n", line);
    }
    
    fclose(fp);
}

// 设置断点 (增强版)
void set_breakpoint(pid_t pid, long addr) {
    // 先检查地址的有效性
    int valid = is_address_valid(pid, addr);
    if (valid == 0) {
        printf("错误: 地址 0x%lx 不在进程的可访问内存范围内\n", addr);
        printf("提示: 使用'maps'命令查看可用的内存映射\n");
        return;
    }
    
    if (valid == 1) {
        printf("警告: 地址 0x%lx 可读但不可执行，这可能不是代码区域\n", addr);
    }
    
    // 保存原始指令，并进行更严格的错误检查
    errno = 0;  // 清除之前的错误码
    long data = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL);
    if (errno != 0) {
        printf("错误: 无法读取地址 0x%lx 的内容\n", addr);
        printf("错误详情: %s\n", strerror(errno));
        return;
    }

    // 写入 int3 指令，并检查错误
    if (ptrace(PTRACE_POKETEXT, pid, addr, (data & ~0xFF) | BREAKPOINT) == -1) {
        printf("错误: 无法在地址 0x%lx 设置断点\n", addr);
        printf("错误详情: %s\n", strerror(errno));
        return;
    }

    // 记录断点
    Breakpoint *bp = malloc(sizeof(Breakpoint));
    bp->address = addr;
    bp->original = data;
    breakpoints[bp_count++] = bp;
    printf("断点已成功设置在地址 0x%lx\n", addr);
}

// 找到指定地址的断点
Breakpoint* find_breakpoint(long addr) {
    for (int i = 0; i < bp_count; i++) {
        if (breakpoints[i]->address == addr) {
            return breakpoints[i];
        }
    }
    return NULL;
}

// 处理程序在断点处停止的情况
void handle_breakpoint(pid_t pid, long bp_addr) {
    Breakpoint* bp = find_breakpoint(bp_addr);
    if (!bp) return;
    
    printf("命中断点 0x%lx\n", bp->address);
    
    // 1. 临时恢复原始指令
    ptrace(PTRACE_POKETEXT, pid, bp->address, bp->original);
    
    // 2. 获取和设置寄存器，将程序计数器重置到断点位置
    struct user_regs_struct regs;
    ptrace(PTRACE_GETREGS, pid, NULL, &regs);
    
    #if defined(__x86_64__) || defined(_M_X64)
    regs.rip = bp->address;
    #elif defined(__i386__) || defined(_M_IX86)
    regs.eip = bp->address;
    #else
    printf("不支持的架构\n");
    return;
    #endif
    
    ptrace(PTRACE_SETREGS, pid, NULL, &regs);
}

// 继续执行程序直到断点或信号
void continue_execution(pid_t pid) {
    struct user_regs_struct regs;
    ptrace(PTRACE_GETREGS, pid, NULL, &regs);
    
    // 获取当前PC值
    long pc = 0;
    #if defined(__x86_64__) || defined(_M_X64)
    pc = regs.rip;
    #elif defined(__i386__) || defined(_M_IX86)
    pc = regs.eip;
    #endif
    
    // 检查当前是否在断点上
    Breakpoint* bp = find_breakpoint(pc);
    if (bp) {
        // 在断点处，先恢复原始指令
        ptrace(PTRACE_POKETEXT, pid, bp->address, bp->original);
        
        // 单步执行原始指令
        ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);
        int status;
        waitpid(pid, &status, 0);
        
        // 恢复断点
        ptrace(PTRACE_POKETEXT, pid, bp->address, (bp->original & ~0xFF) | BREAKPOINT);
    }
    
    // 继续执行程序
    ptrace(PTRACE_CONT, pid, NULL, NULL);
    
    // 等待子进程停止或终止
    int status;
    waitpid(pid, &status, 0);
    
    if (WIFSTOPPED(status)) {
        int signal = WSTOPSIG(status);
        if (signal == SIGTRAP) {
            // 查找是否命中断点
            ptrace(PTRACE_GETREGS, pid, NULL, &regs);
            
            #if defined(__x86_64__) || defined(_M_X64)
            pc = regs.rip - 1; // 断点触发后PC会指向下一条指令
            #elif defined(__i386__) || defined(_M_IX86)
            pc = regs.eip - 1;
            #endif
            
            bp = find_breakpoint(pc);
            if (bp) {
                handle_breakpoint(pid, pc);
            } else {
                printf("收到SIGTRAP信号（可能是系统调用或单步执行）\n");
            }
        } else {
            printf("程序收到信号 %d 停止执行\n", signal);
        }
    } else if (WIFEXITED(status)) {
        printf("程序正常退出，返回值: %d\n", WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)) {
        printf("程序因信号 %d 终止\n", WTERMSIG(status));
    }
    
    // 显示当前寄存器状态
    show_registers(pid);
}

// 单步执行
void single_step(pid_t pid) {
    ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);
    waitpid(pid, NULL, 0);
}

// 显示寄存器状态
void show_registers(pid_t pid) {
    struct user_regs_struct regs;
    if (ptrace(PTRACE_GETREGS, pid, NULL, &regs) == -1) {
        perror("ptrace getregs");
        return;
    }
    
    #if defined(__x86_64__) || defined(_M_X64)
    // 64位架构下的寄存器
    printf("通用寄存器:\n");
    printf("RAX: 0x%llx\tRBX: 0x%llx\tRCX: 0x%llx\n", regs.rax, regs.rbx, regs.rcx);
    printf("RDX: 0x%llx\tRSI: 0x%llx\tRDI: 0x%llx\n", regs.rdx, regs.rsi, regs.rdi);
    printf("RBP: 0x%llx\tRSP: 0x%llx\tRIP: 0x%llx\n", regs.rbp, regs.rsp, regs.rip);
    printf("R8:  0x%llx\tR9:  0x%llx\tR10: 0x%llx\n", regs.r8, regs.r9, regs.r10);
    printf("R11: 0x%llx\tR12: 0x%llx\tR13: 0x%llx\n", regs.r11, regs.r12, regs.r13);
    printf("R14: 0x%llx\tR15: 0x%llx\n", regs.r14, regs.r15);
    
    printf("\n标志寄存器:\n");
    printf("EFLAGS: 0x%llx\n", regs.eflags);
    
    printf("\n段寄存器:\n");
    printf("CS: 0x%llx\tSS: 0x%llx\tDS: 0x%llx\n", regs.cs, regs.ss, regs.ds);
    printf("ES: 0x%llx\tFS: 0x%llx\tGS: 0x%llx\n", regs.es, regs.fs, regs.gs);
    
    printf("\n其他寄存器:\n");
    printf("FS_BASE: 0x%llx\tGS_BASE: 0x%llx\n", regs.fs_base, regs.gs_base);
    printf("ORIG_RAX: 0x%llx\n", regs.orig_rax);

    // 尝试获取浮点寄存器
    struct user_fpregs_struct fpregs;
    if (ptrace(PTRACE_GETFPREGS, pid, NULL, &fpregs) != -1) {
        printf("\n浮点寄存器状态:\n");
        printf("FP控制字: 0x%x\n", fpregs.cwd);
        printf("FP状态字: 0x%x\n", fpregs.swd);
        printf("FP标签字: 0x%x\n", fpregs.ftw);
    }
    
    #elif defined(__i386__) || defined(_M_IX86)
    // 32位架构下的寄存器
    printf("通用寄存器:\n");
    printf("EAX: 0x%lx\tEBX: 0x%lx\tECX: 0x%lx\n", regs.eax, regs.ebx, regs.ecx);
    printf("EDX: 0x%lx\tESI: 0x%lx\tEDI: 0x%lx\n", regs.edx, regs.esi, regs.edi);
    printf("EBP: 0x%lx\tESP: 0x%lx\tEIP: 0x%lx\n", regs.ebp, regs.esp, regs.eip);

    printf("\n标志寄存器:\n");
    printf("EFLAGS: 0x%lx\n", regs.eflags);
    
    printf("\n段寄存器:\n");
    printf("CS: 0x%lx\tSS: 0x%lx\tDS: 0x%lx\n", regs.xcs, regs.xss, regs.xds);
    printf("ES: 0x%lx\tFS: 0x%lx\tGS: 0x%lx\n", regs.xes, regs.xfs, regs.xgs);
    
    printf("\n其他寄存器:\n");
    printf("ORIG_EAX: 0x%lx\n", regs.orig_eax);
    #else
    // 其他架构可能需要查看系统定义的结构体成员
    printf("不支持的架构\n");
    #endif
}

// 读取内存内容
void read_memory(pid_t pid, long addr, size_t len, void *buffer) {
    long *ptr = (long*)buffer;
    size_t i = 0;
    
    while (i < len / sizeof(long)) {
        ptr[i] = ptrace(PTRACE_PEEKTEXT, pid, addr + i * sizeof(long), NULL);
        i++;
    }
}

// 写入内存内容
void write_memory(pid_t pid, long addr, size_t len, void *buffer) {
    long *ptr = (long*)buffer;
    size_t i = 0;
    
    while (i < len / sizeof(long)) {
        ptrace(PTRACE_POKETEXT, pid, addr + i * sizeof(long), ptr[i]);
        i++;
    }
}

// 主调试循环
void debug_loop(pid_t pid) {
    int wait_status;
    while(1) {
        // 等待用户输入命令
        printf("(mini-gdb) ");
        char cmd[32];
        scanf("%s", cmd);

        if (strcmp(cmd, "break") == 0 || strcmp(cmd, "b") == 0) {
            long addr;
            scanf("%lx", &addr);
            set_breakpoint(pid, addr);
        } else if (strcmp(cmd, "run") == 0 || strcmp(cmd, "r") == 0 || 
                  strcmp(cmd, "continue") == 0 || strcmp(cmd, "c") == 0) {
            // 新命令：继续执行直到断点
            continue_execution(pid);
        } else if (strcmp(cmd, "step") == 0 || strcmp(cmd, "s") == 0) {
            single_step(pid);
            show_registers(pid);
        } else if (strcmp(cmd, "regs") == 0 || strcmp(cmd, "r") == 0) {
            show_registers(pid);
        } else if (strcmp(cmd, "mem") == 0 || strcmp(cmd, "m") == 0) {
            long addr;
            scanf("%lx", &addr);
            
            // 检查地址有效性
            int valid = is_address_valid(pid, addr);
            if (valid == 0) {
                printf("错误: 地址 0x%lx 不在进程的可访问内存范围内\n", addr);
                continue;
            }
            
            // 读取16个字节
            unsigned char buffer[16] = {0};
            int success = 1;
            
            // 按long大小的块读取内存
            for (int i = 0; i < sizeof(buffer); i += sizeof(long)) {
                errno = 0;
                long val = ptrace(PTRACE_PEEKTEXT, pid, addr + i, NULL);
                if (errno != 0) {
                    printf("错误: 无法读取地址 0x%lx 的内容\n", addr + i);
                    printf("错误详情: %s\n", strerror(errno));
                    success = 0;
                    break;
                }
                // 复制到buffer，注意不要越界
                int bytes_to_copy = sizeof(long);
                if (i + bytes_to_copy > sizeof(buffer)) {
                    bytes_to_copy = sizeof(buffer) - i;
                }
                memcpy(buffer + i, &val, bytes_to_copy);
            }
            
            if (success) {
                // 打印内存内容
                printf("0x%lx: ", addr);
                
                // 打印十六进制值
                for (int i = 0; i < sizeof(buffer); i++) {
                    printf("%02x ", buffer[i]);
                    // 每8个字节添加一个额外的空格以提高可读性
                    if (i == 7)
                        printf(" ");
                }
                
                // 打印ASCII表示
                printf(" |");
                for (int i = 0; i < sizeof(buffer); i++) {
                    // 只打印可打印的ASCII字符，其他替换为点
                    if (buffer[i] >= 32 && buffer[i] <= 126) {
                        printf("%c", buffer[i]);
                    } else {
                        printf(".");
                    }
                }
                printf("|\n");
            }
        } else if (strcmp(cmd, "maps") == 0) {
            // 新命令：显示进程内存映射
            show_memory_maps(pid);
        } else if (strcmp(cmd, "help") == 0 || strcmp(cmd, "h") == 0) {
            printf("可用命令:\n");
            printf("  break/b 0xADDRESS - 在指定地址设置断点\n");
            printf("  run/r, continue/c - 继续执行程序直到断点或信号\n");
            printf("  step/s            - 单步执行程序\n");
            printf("  regs/r            - 显示所有寄存器的内容\n");
            printf("  mem/m 0xADDRESS   - 查看指定内存地址的内容\n");
            printf("  maps              - 显示进程内存映射\n");
            printf("  help/h            - 显示此帮助信息\n");
            printf("  quit/q            - 退出调试器\n");
        } else if (strcmp(cmd, "quit") == 0 || strcmp(cmd, "q") == 0) {
            break;
        } else {
            printf("未知命令，输入 'help' 获取帮助\n");
        }
    }
}

int main(int argc, char **argv) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <program>\n", argv[0]);
        return 1;
    }

    pid_t pid = fork();
    if (pid == 0) {
        // 子进程：被调试的程序
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl(argv[1], argv[1], NULL);
    } else {
        // 父进程：调试器主逻辑
        waitpid(pid, NULL, 0);  // 等待初始暂停
        debug_loop(pid);
    }
    return 0;
}
